package com.example.demo.service;

import com.example.demo.domain.dao.OptionalCourse;
import com.example.demo.domain.dto.PhysicalExperiment;
import com.example.demo.domain.dto.UserOptionalCourse;
import com.example.demo.domain.result.GradesResult;
import com.example.demo.domain.result.helper.CourseGrade;
import com.example.demo.domain.send.DingMessage;
import com.example.demo.mapper.ScheduleDao;
import com.example.demo.domain.dao.DeletedSchedule;
import com.example.demo.domain.dao.OwnSchedule;
import com.example.demo.domain.dao.OwnScheduleTime;
import com.example.demo.domain.result.AutoIncreasedId;
import com.example.demo.domain.result.helper.Course;
import com.example.demo.domain.dao.HbutClassSchedule;
import com.example.demo.domain.dao.HbutEtcClassSchedule;
import com.example.demo.domain.result.helper.DeletedScheduleResult;
import com.example.demo.domain.result.helper.Time;
import com.example.demo.domain.result.ScheduleResult;
import com.example.demo.exception.PasswordErrorException;
import com.example.demo.exception.ScheduleNotExistException;
import com.example.demo.physicalExperiment.mapper.PhysicalCourseDao;
import com.example.demo.utils.ParseUtil;
import com.google.common.reflect.TypeToken;
import com.google.gson.Gson;
import java.net.SocketTimeoutException;
import java.sql.Date;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import lombok.extern.slf4j.Slf4j;
import org.apache.catalina.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.springframework.transaction.annotation.Transactional;


/**
 * 查询班级课表
 *
 * @author lizijian
 */
@Slf4j
@Service
public class ScheduleService {

  @Autowired
  private Gson gson;

  @Autowired
  private ScheduleDao scheduleDao;

  @Autowired
  private PhysicalCourseDao physicalCourseDao;

  @Autowired
  private GradesService gradesService;


  public void insertSchedule(String college, String grade, String className, String data) {
    scheduleDao.insertSchedule(className, data, college, grade);
  }

  /**
   * 爬取指定班级的课表信息
   *
   * @param className 班级名
   */
  public void insertSchedule(String className) throws PasswordErrorException, IOException {
    ArrayList<Course> courses = ParseUtil.getClassSchedule(className);
    if (courses.size() > 0) {
      String jsonData = gson.toJson(courses);
      String college = ParseUtil.getClassCollege(className);
      insertSchedule(college, className.substring(0, 2), className, jsonData);
      log.info("更新" + className + "课表成功");
    } else {
      log.info(className + ":课表为空");
    }
  }


  /**
   * 使用班级名获取湖工大课表 如果数据库不存在课表则从教务系统获取
   */
  public ScheduleResult queryHbutScheduleByClassName(String className)
      throws PasswordErrorException, IOException, ScheduleNotExistException {
    HbutClassSchedule hbutClassSchedule = scheduleDao.queryScheduleByClassName(className);
    if (hbutClassSchedule == null) {
      ArrayList<Course> courses = ParseUtil.getClassSchedule(className);
      String college = ParseUtil.getClassCollege(className);
      if (courses.size() > 0) {
        scheduleDao.insertSchedule(className, gson.toJson(courses), college,
            className.substring(0, 2));
        hbutClassSchedule = scheduleDao.queryScheduleByClassName(className);
      }
    }
    if (hbutClassSchedule == null) {
      throw new ScheduleNotExistException(className + ":班级课表不存在");
    }
    //课表数据为空
    if (hbutClassSchedule.getData() == null) {
      return new ScheduleResult(className, new ArrayList<>(), null);
    }
    ScheduleResult result = new ScheduleResult(className, Arrays.asList(
        gson.fromJson(hbutClassSchedule.getData(), Course[].class)),
        new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(
            hbutClassSchedule.getUpdateTime().getTime()));
    return result;
  }


  public ScheduleResult getHbutEtcSchedule(Integer classId) throws ScheduleNotExistException {
    HbutEtcClassSchedule hbutEtcClassSchedule = scheduleDao.queryHbutEtcScheduleByClassId(classId);
    if (hbutEtcClassSchedule == null) {
      throw new ScheduleNotExistException(classId + ":湖工大工程学院课表不存在");
    }
    ScheduleResult result = new ScheduleResult(hbutEtcClassSchedule.getName(), Arrays.asList(
        gson.fromJson(hbutEtcClassSchedule.getData(), Course[].class)),
        new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(
            hbutEtcClassSchedule.getUpdateTime().getTime()));
    return result;
  }

  public void addHbutEtcSchedule(Integer classId, String className, String data) {
    if (scheduleDao.queryHbutEtcScheduleByClassId(classId) == null) {
      scheduleDao.addHbutEtcSchedule(classId, data, className);
    } else {
      scheduleDao.updateHbutEtcSchedule(classId, data, new Date(System.currentTimeMillis()));
    }
  }


  /**
   * 将参数进行模糊查询处理
   */
  public List<String> fuzzyQueryClassName(String className) {
    if (className == null) {
      return null;
    }
    char[] chars = className.toCharArray();
    String result = "%";
    for (char aChar : chars) {
      result += String.valueOf(aChar);
      result += "%";
    }
    return scheduleDao.fuzzyQueryClassName(result);
  }

  /**
   * 清除课表
   */
  public void deleteSchedule() {
    scheduleDao.deleteSchedule();
  }

  /**
   * 是否存在同名自定义课程
   */
  public boolean existSameNameSchedule(Integer userId, String scheduleName) {
    return scheduleDao.getOwnScheduleByName(userId, scheduleName) != null;
  }

  @Transactional(rollbackFor = Exception.class)
  public void addOwnSchedule(Integer userId, String name, String address,
      String weeks, Integer day, Integer lesson) {
    AutoIncreasedId autoIncreasedId = new AutoIncreasedId();
    scheduleDao.addOwnSchedule(autoIncreasedId, userId, name);
    scheduleDao.addOwnScheduleTime(autoIncreasedId.getId(), address, weeks, day, lesson);
  }

  public void addOwnScheduleTime(Integer ownScheduleId, String address, String weeks, Integer day,
      Integer lesson) {
    scheduleDao.addOwnScheduleTime(ownScheduleId, address, weeks, day, lesson);
  }

  public void updateOwnScheduleTime(Integer scheduleTimeId, String address, String weeks,
      Integer day, Integer lesson) {
    scheduleDao.updateOwnScheduleTime(scheduleTimeId, address, weeks, day, lesson);
  }

  public void deleteOwnScheduleByScheduleId(Integer scheduleId) {
    scheduleDao.deleteOwnScheduleByScheduleId(scheduleId);
    scheduleDao.deleteOwnScheduleTimeByScheduleId(scheduleId);
  }

  public void deleteOwnScheduleByUserId(Integer userId) {
    scheduleDao.deleteOwnScheduleTimeByUserId(userId);
    scheduleDao.deleteOwnScheduleByUserId(userId);
  }

  /**
   * 增加自定义课程
   */
  public ScheduleResult addSchedule(Integer userId, ScheduleResult result) {
    List<OwnSchedule> ownScheduleList = scheduleDao.getOwnScheduleByUserId(userId);
    ArrayList<Course> ownCourses = new ArrayList<>(10);
    for (OwnSchedule ownSchedule : ownScheduleList) {
      List<OwnScheduleTime> ownScheduleTimes = scheduleDao.getOwnScheduleTimeByScheduleId(
          ownSchedule.getId());
      ArrayList<Time> courseTime = new ArrayList<>(5);
      for (OwnScheduleTime ownScheduleTime : ownScheduleTimes) {
        Set<Integer> weeks = gson.fromJson(ownScheduleTime.getWeeks(),
            new TypeToken<Set<Integer>>() {
            }.getType());
        courseTime.add(new Time(weeks, ownScheduleTime.getLesson(), ownScheduleTime.getDay(), null,
            ownScheduleTime.getAddress()));
      }
      ownCourses.add(new Course(ownSchedule.getId(), ownSchedule.getName(), courseTime));
    }
    result.getSchedule().addAll(ownCourses);
    return result;
  }

  /**
   * 删除教务课程
   */
  public ScheduleResult deleteSchedule(Integer userId, ScheduleResult result) {
    List<Course> courses = result.getSchedule();
    courses = new ArrayList<>(courses);
    List<DeletedSchedule> deletedSchedules = scheduleDao.getDeletedScheduleByUserId(
        userId);
    for (DeletedSchedule deletedSchedule : deletedSchedules) {
      courses.removeIf(
          course -> course.getCourseName().equals(deletedSchedule.getScheduleName()));
    }
    result.setSchedule(courses);
    return result;
  }

  public ScheduleResult addPhysicalExperiment(String username, ScheduleResult result) {
    List<Course> courses = result.getSchedule();
    List<PhysicalExperiment> physicalExperiments = physicalCourseDao.getCourseBySchoolId(username);
    for (PhysicalExperiment physicalExperiment : physicalExperiments) {
      ArrayList<Time> timeArrayList = new ArrayList<>(1);
      Set<Integer> week = new HashSet<>(4);
      week.add(physicalExperiment.getWeek());
      Integer lesson = physicalExperiment.getLesson();
      System.out.println(physicalExperiment.getCourseName() + physicalExperiment.getLesson());
      if (lesson.equals(3)) {
        lesson = 2;
      } else if (lesson.equals(5)) {
        lesson = 3;
      } else if (lesson.equals(7)) {
        lesson = 4;
      } else if (lesson.equals(9)) {
        lesson = 5;
      } else if (lesson.equals(0)) {
        lesson = 5;
        physicalExperiment.setCourseName("20:00_" + physicalExperiment.getCourseName());
      }
      timeArrayList.add(new Time(week, lesson,
          physicalExperiment.getDay(), null, "6-" + physicalExperiment.getAddress()));
      courses.add(new Course(physicalExperiment.getCourseName(), timeArrayList));
    }
    if (physicalExperiments.size() > 0) {
      result.setWithPhysical(true);
    }
    return result;
  }


  public ScheduleResult mixSchedule(Integer userId, ScheduleResult result, String username,
      String password, Integer campusId, boolean isMyClass) {
    ScheduleResult scheduleResult = deleteSchedule(userId, result);
    if (username != null && isMyClass) {
      try {
        scheduleResult = addPhysicalExperiment(username, scheduleResult);
      } catch (Exception e) {
        scheduleResult.setWithPhysical(false);
        scheduleResult.setPhysicalMessage("查询物理实验失败,系统错误");
        log.error(username + "-" + password + "-增加物理实验失败");
        log.error("errorMessage" + e.getMessage());
        SendDingMessageService.sendErrorMessage(new DingMessage(
            username + "-" + password + "-增加物理实验失败,errorMessage:" + e.getMessage()));
      }
    }
    if (campusId.equals(1) && username != null && password != null && isMyClass) {
      try {
        scheduleResult = addOptionalCourse(scheduleResult, username, password);
        scheduleResult= addArbitrarilyCourse(scheduleResult,username,password);
      } catch (PasswordErrorException passwordErrorException) {
        scheduleResult.setWithOptional(false);
        scheduleResult.setOptionalMessage("密码错误");
        log.error(username + "-" + password + "-增加选修课失败");
        log.error("errorMessage" + passwordErrorException.getMessage());
      } catch (SocketTimeoutException socketTimeoutException) {
        scheduleResult.setWithOptional(false);
        scheduleResult.setOptionalMessage("连接超时,请稍后再试");
        log.error(username + "-" + password + "-增加选修课失败");
        log.error("errorMessage" + socketTimeoutException.getMessage());
      } catch (Exception e) {
        scheduleResult.setWithOptional(false);
        scheduleResult.setOptionalMessage("系统错误");
        log.error(username + "-" + password + "-增加选修课失败");
        log.error("errorMessage" + e.getMessage());
        SendDingMessageService.sendErrorMessage(
            new DingMessage(username + "-" + password + "-增加选修课失败,errorMessage:" + e.getMessage()));
      }
    }
    return addSchedule(userId, scheduleResult);
  }

  public void addOptionCourseToSchedule(List<Course> schedule,
      List<OptionalCourse> optionalCourses) {
    for (OptionalCourse optionalCourse : optionalCourses) {
      ArrayList<Time> timeArrayList = new ArrayList<>(1);
      Set<Integer> weeks = gson.fromJson(optionalCourse.getWeeks(), new TypeToken<Set<Integer>>() {
      }.getType());
      timeArrayList.add(new Time(weeks, optionalCourse.getLesson(),
          optionalCourse.getDay(), optionalCourse.getTeacherName(),
          optionalCourse.getAddress()));
      schedule.add(new Course(optionalCourse.getCourseName(), timeArrayList));
    }
  }

  public void addUserOptionCourseToSchedule(List<Course> schedule,
      List<UserOptionalCourse> optionalCourses) {
    for (UserOptionalCourse optionalCourse : optionalCourses) {
      ArrayList<Time> timeArrayList = new ArrayList<>(1);
      Set<Integer> weeks = gson.fromJson(optionalCourse.getWeeks(), new TypeToken<Set<Integer>>() {
      }.getType());
      timeArrayList.add(new Time(weeks, optionalCourse.getLesson(),
          optionalCourse.getDay(), optionalCourse.getTeacherName(),
          optionalCourse.getAddress()));
      schedule.add(new Course(optionalCourse.getCourseName(), timeArrayList));
    }
  }

  public ScheduleResult getOptionalCourseFromRemote(ScheduleResult scheduleResult, String username,
      String password)
      throws PasswordErrorException, IOException {
    ArrayList<String> courses = new ArrayList<>(2);
    GradesResult grades = gradesService.getGrades(username, password, null);
    List<CourseGrade> detailedGrades = grades.getDetailedGrades();
    for (CourseGrade detailedGrade : detailedGrades) {
      if ("公选课".equals(detailedGrade.getCourseType())) {
        log.info(username + ":拥有公选课-" + detailedGrade.getCourseName());
        courses.add(detailedGrade.getCourseName());
      }
    }
    List<Course> schedule = scheduleResult.getSchedule();
    ArrayList<String> courseNames = new ArrayList<>(5);
    for (String course : courses) {
      List<OptionalCourse> optionalCourses = scheduleDao.getOptionalCourseByCourseName(
          course);
      if (optionalCourses.size() > 0) {
        scheduleResult.setWithOptional(true);
        courseNames.add(course);
      }
      addOptionCourseToSchedule(schedule, optionalCourses);
    }
    scheduleDao.deleteUserOptionalCourse(username);
    if (courseNames.size() == 0) {
      scheduleDao.addUserOptionalCourse(username, "无");
    } else {
      for (String courseName : courseNames) {
        scheduleDao.addUserOptionalCourse(username, courseName);
      }
    }
    return scheduleResult;
  }

  public ScheduleResult getArbitrarilyCourseFromRemote(ScheduleResult scheduleResult,
      String username, String password)
      throws Exception {
    ArrayList<OptionalCourse> arbitrarilyCourses = ParseUtil.getArbitrarilyCourse(username,
        password);
    arbitrarilyCourses.removeIf(item->item.getCourseName().startsWith("体育"));
    for (OptionalCourse course : arbitrarilyCourses) {
      log.info(username + ":拥有任选课-" + course.getCourseName());
    }
    if (arbitrarilyCourses.size() > 0) {
      scheduleResult.setWithOptional(true);
      addOptionCourseToSchedule(scheduleResult.getSchedule(), arbitrarilyCourses);
    }
    scheduleDao.deleteArbitraryCourse(username);
    if (arbitrarilyCourses.size() == 0) {
      scheduleDao.addUserArbitraryCourse(username, "无", "", "", 1, 1);
    } else {
      for (OptionalCourse arbitrarilyCourse : arbitrarilyCourses) {
        scheduleDao.addUserArbitraryCourse(username, arbitrarilyCourse.getCourseName(),
            arbitrarilyCourse.getAddress(), arbitrarilyCourse.getWeeks(),
            arbitrarilyCourse.getDay(), arbitrarilyCourse.getLesson());
      }
    }
    return scheduleResult;
  }

  public ScheduleResult addArbitrarilyCourse(ScheduleResult scheduleResult, String username,
      String password) throws Exception {
    List<UserOptionalCourse> arbitraryCourses = scheduleDao.getArbitraryCourse(username);
    if (arbitraryCourses.size() == 0) {
      return getArbitrarilyCourseFromRemote(scheduleResult, username, password);
    }
    java.util.Date updateTime = arbitraryCourses.get(0).getUpdateTime();
    //缓存时间超过
    if (System.currentTimeMillis() - updateTime.getTime() > 86400000) {
      try {
        return getArbitrarilyCourseFromRemote(scheduleResult, username, password);
      } catch (PasswordErrorException passwordErrorException) {
        scheduleResult.setOptionalMessage("密码错误");
        log.error(username + "-" + password + "-增加选修课失败");
        log.error("errorMessage" + passwordErrorException.getMessage());
      } catch (SocketTimeoutException socketTimeoutException) {
        scheduleResult.setOptionalMessage("连接超时,请稍后再试");
        log.error(username + "-" + password + "-增加选修课失败");
        log.error("errorMessage" + socketTimeoutException.getMessage());
      } catch (Exception e) {
        System.out.println(Arrays.toString(e.getStackTrace()));
        scheduleResult.setOptionalMessage("系统错误");
        log.error(username + "-" + password + "-增加选修课失败");
        log.error("errorMessage" + e.getMessage());
        SendDingMessageService.sendErrorMessage(new DingMessage(
            username + "-" + password + "-增加选修课失败,errorMessage:" + e.getMessage()));
      }
    }
    if(arbitraryCourses.size()>0&&!(arbitraryCourses.size()==1&& "无".equals(
        arbitraryCourses.get(0).getCourseName()))){
      addUserOptionCourseToSchedule(scheduleResult.getSchedule(), arbitraryCourses);
      scheduleResult.setWithOptional(true);
    }
    return scheduleResult;
  }

  public ScheduleResult addOptionalCourse(ScheduleResult scheduleResult, String username,
      String password)
      throws PasswordErrorException, IOException {
    List<UserOptionalCourse> optionalCourses = scheduleDao.getOptionalCourseByUsername(
        username);
    //数据库中没有缓存选修课
    if (optionalCourses.size() == 0) {
      return getOptionalCourseFromRemote(scheduleResult, username, password);
    }
    UserOptionalCourse optionalCourse = optionalCourses.get(0);
    java.util.Date updateTime = optionalCourse.getUpdateTime();
    //缓存时间超过
    if (System.currentTimeMillis() - updateTime.getTime() > 86400000) {
      try {
        return getOptionalCourseFromRemote(scheduleResult, username, password);
      } catch (PasswordErrorException passwordErrorException) {
        scheduleResult.setOptionalMessage("密码错误");
        log.error(username + "-" + password + "-增加选修课失败");
        log.error("errorMessage" + passwordErrorException.getMessage());
      } catch (SocketTimeoutException socketTimeoutException) {
        scheduleResult.setOptionalMessage("连接超时,请稍后再试");
        log.error(username + "-" + password + "-增加选修课失败");
        log.error("errorMessage" + socketTimeoutException.getMessage());
      } catch (Exception e) {
        System.out.println(Arrays.toString(e.getStackTrace()));
        scheduleResult.setOptionalMessage("系统错误");
        log.error(username + "-" + password + "-增加选修课失败");
        log.error("errorMessage" + e.getMessage());
        SendDingMessageService.sendErrorMessage(new DingMessage(
            username + "-" + password + "-增加选修课失败,errorMessage:" + e.getMessage()));
      }
    }
    if(optionalCourses.size()>0&&!(optionalCourses.size()==1&& "无".equals(
        optionalCourses.get(0).getCourseName()))){
      addUserOptionCourseToSchedule(scheduleResult.getSchedule(), optionalCourses);
      scheduleResult.setWithOptional(true);
    }
    return scheduleResult;
  }

  public void addDeletedSchedule(String scheduleName, Integer userId) {
    scheduleDao.addDeletedSchedule(scheduleName, userId);
  }


  public List<DeletedScheduleResult> getDeletedSchedule(Integer userId) {
    List<DeletedSchedule> deletedSchedules = scheduleDao.getDeletedScheduleByUserId(
        userId);
    ArrayList<DeletedScheduleResult> deletedScheduleResults = new ArrayList<>(
        deletedSchedules.size());
    for (DeletedSchedule deletedSchedule : deletedSchedules) {
      deletedScheduleResults.add(new DeletedScheduleResult(deletedSchedule.getId(),
          deletedSchedule.getScheduleName()));
    }
    return deletedScheduleResults;
  }

  public void recoverDeletedSchedule(Integer deletedScheduleId) {
    scheduleDao.recoverDeletedSchedule(deletedScheduleId);
  }

}
